#ifndef __CROSSFIRE_BSP
#define __CROSSFIRE_BSP

enum ECF_TraceType
{
  Ray,
  Box,
  Shpeare
};

enum BSP_LUMP
{
  lEntities,
  lTextures,
  lPlanes,
  lNodes,
  lLeafs,
  lLeaffaces,
  lLeafbrushes,
  lModels,
  lBrushes,
  lBrushSides,
  lVertcies,
  lIndices,
  lEffects,
  lFaces,
  lLightmaps,
  lLightvols,
  lVisdata
};

#define BSP_CUSTOM_VERTEX_FVF (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1|D3DFVF_TEX2)
#define BSP_FLOAT_EPSILON 0.0001
typedef WORD BSP_INDEX;
typedef int BSP_LEAFFACES;
typedef int BSP_LEAFBRUSHES;

struct BSP_ENTRY
{
  int offset;
  int length;
};

struct BSP_HEADER
{
  char idNumber[4];
  int version;
  BSP_ENTRY entries[17];
};

struct BSP_ENTITY
{
  char *ents;	
};

struct BSP_TEXTURE
{
  char name[64];
  int flags;
  int contents;
};

struct BSP_PLANE
{
  D3DXVECTOR3 normal;
  float dist;
};

struct BSP_NODE
{
  int plane;
  int children[2];
  int mins[3];
  int maxs[3];
};

struct BSP_LEAF
{
  int cluster;
  int area;
  int mins[3];
  int maxs[3];
  int leafface;
  int n_leaffaces;
  int leafbrush;
  int n_leafbrushes;
};


struct BSP_MODEL
{
  float mins[3];
  float maxs[3];
  int face;
  int n_faces;
  int brush;
  int n_brushes;
};

struct BSP_BRUSH
{
  int brushside;
  int n_brushsides;
  int texture;
};

struct BSP_BRUSHSIDES
{
  int plane;
  int texture;
};

struct BSP_VERTEX
{
  float position[3];
  float texcoord[2];
  float lightmap[2];
  float normal[3];
  BYTE color[4];
};


struct BSP_EFFECT
{
  char name[64];
  int brush;
  int unknown;
};

struct BSP_FACE
{
  int texture;
  int effect;
  int type;
  int vertex;
  int n_verts;
  int meshvert;
  int n_meshverts;
  int lm_index;
  int lm_start[2];
  int lm_size[2];
  float lm_origin[3];
  float lm_vecs[2][3];
  float normal[3];
  int size[2];
};

struct BSP_LIGHTMAP
{
  byte imageBits[128][128][3];
};

struct BSP_VISDATA
{
  int n_vecs;
  int vecSize;
  byte *vecs; 
};


struct BSP_CUSTOM_VERTEX
{
  float x, y, z;
  float nx, ny, nz;
  D3DCOLOR color;
  float tu, tv;
  float lu, lv;
};

struct SCF_CollisionInfo
{
  bool IsCollision;
  bool TryStep;
  bool IsGround;

  float TraceRatio;
  float TraceRadius;

  D3DXVECTOR3 Normal;
  D3DXVECTOR3 Position;
  D3DXVECTOR3 TraceMaxs;
  D3DXVECTOR3 TraceMins;
  D3DXVECTOR3 Extents;

  ECF_TraceType TraceType;
};

enum BSP_RENDER_STATES
{
  rShowLightMap = 0,
  rShowTextureMap,
  rUsePVS,
};

class CBitset: public CCF_EngineElement 

{
public:

  CBitset() : m_bits(0), m_size(0) {}

  ~CBitset() 
  {
    if(m_bits) 
    {
      delete m_bits;
      m_bits = NULL;
    }
  }

  void Resize(int count) 
  { 
    m_size = count/32 + 1;

    if(m_bits) 
    {
      delete m_bits;
      m_bits = 0;
    }

    m_bits = new unsigned int[m_size];
    ClearAll();
  }

  void Set(int i) 
  {
    m_bits[i >> 5] |= (1 << (i & 31));
  }

  int On(int i) 
  {
    return m_bits[i >> 5] & (1 << (i & 31 ));
  }

  void Clear(int i) 
  {
    m_bits[i >> 5] &= ~(1 << (i & 31));
  }

  void ClearAll() 
  {
    memset(m_bits, 0, sizeof(unsigned int) * m_size);
  }

  void SetAll()
  {
    memset(m_bits, 4294967295, sizeof(unsigned int) * m_size);
  }

private:
  unsigned int *m_bits;
  int m_size;
};


class CCF_Bsp: public CCF_EngineElement 
{
private:
  CCF_DrawRenderer* m_renderer;

  LPDIRECT3DDEVICE9 m_pDevice;
  LPDIRECT3DVERTEXBUFFER9 m_pVB;
  LPDIRECT3DINDEXBUFFER9 m_pIB;
  LPDIRECT3DTEXTURE9 *m_pTextureMaps;
  LPDIRECT3DTEXTURE9 *m_pLightMaps;

  CBitset DrawnFaces;

  BSP_INDEX *m_pIndices;
  BSP_VERTEX *m_pVertices;
  BSP_LIGHTMAP *m_pLightmap;
  BSP_TEXTURE *m_pTextures;
  BSP_FACE *m_pFaces;
  BSP_PLANE *m_pPlanes;
  BSP_NODE *m_pNodes;
  BSP_LEAF *m_pLeaves;
  BSP_LEAFFACES *m_pLeafFaces;
  BSP_BRUSH *m_pBrushes;
  BSP_BRUSHSIDES *m_pBrushSides;
  BSP_LEAFBRUSHES *m_pLeafBrushes;
  BSP_VISDATA m_sVisData;

  int m_nIndicesCount;
  int m_nVerticesCount;
  int m_nLightmapCount;
  int m_nTexturesCount;
  int m_nFacesCount;
  int m_nPlanesCount;
  int m_nNodesCount;
  int m_nLeavesCount;
  int m_nLeafFacesCount;
	int m_nBrushesCount;
	int m_nBrushSidesCount;
	int m_nLeafBrushesCount;

  bool m_pRenderState[3];

  BOOL Load(const LPWSTR strFileName);
  INT FindLeaf(D3DXVECTOR3 camPos);
  BOOL ClusterVisible(int visCluster, int testCluster);
  void Build();

  void TryToStep(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo);
  void Trace(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo);
  void CheckNode(int nodeIndex, float startRatio, float endRatio, 
    D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo);
  void CheckBrush(BSP_BRUSH *pBrush, D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo *collInfo );

public:
  CCF_Bsp();
  ~CCF_Bsp();

  BOOL Initialize(CCF_DrawRenderer* renderer, LPWSTR filename );
  BOOL IsHit( D3DXVECTOR3 start, D3DXVECTOR3 end );

  void Render(CCF_Camera* Camera);

  void SetRenderState(BSP_RENDER_STATES state, BOOL value);
  BOOL GetRenderState(BSP_RENDER_STATES state);

  void TraceRay(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo);
  void TraceSphere(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, float radius, SCF_CollisionInfo* collInfo);
  void TraceBox(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, D3DXVECTOR3 vMin, D3DXVECTOR3 vMax, SCF_CollisionInfo* collInfo);
};
#endif